//! World inputs from player, user, etc.

use craftdig_math::{ChunkCoord, Vec3f};

use super::ChunkState;
use crate::{block::action::ActionData, player::id::PlayerId};

/// Collection of inputs on a particular tick
#[derive(Clone, Debug, Default)]
pub struct TickInput {
    player_joins: Vec<PlayerJoin>,
    player_inputs: Vec<PlayerInput>,
    new_chunks: Vec<NewChunk>,
    input_closed: bool,
}

impl TickInput {
    pub fn new_empty() -> Self {
        Self::default()
    }

    pub fn player_joins(&self) -> &[PlayerJoin] {
        &self.player_joins
    }

    pub fn player_inputs(&self) -> &[PlayerInput] {
        &self.player_inputs
    }

    pub fn new_chunks(&self) -> &[NewChunk] {
        &self.new_chunks
    }

    pub fn input_closed(&self) -> bool {
        self.input_closed
    }

    // Modifiers.

    pub fn add_join(&mut self, pj: PlayerJoin) {
        self.player_joins.push(pj);
    }

    pub fn add_player_inp(&mut self, pi: PlayerInput) {
        self.player_inputs.push(pi);
    }

    pub fn add_new_chunk(&mut self, chunk: NewChunk) {
        self.new_chunks.push(chunk);
    }

    pub fn mark_closed(&mut self) {
        self.input_closed = true;
    }
}

#[derive(Clone, Debug)]
pub struct PlayerJoin {
    id: PlayerId,
    location: Vec3f,
}

impl PlayerJoin {
    pub fn new(id: PlayerId, location: Vec3f) -> Self {
        Self { id, location }
    }

    pub fn id(&self) -> PlayerId {
        self.id
    }

    pub fn location(&self) -> Vec3f {
        self.location
    }
}

#[derive(Clone, Debug)]
pub struct PlayerInput {
    id: PlayerId,

    /// Movement intent, in world space.
    move_intent: Option<Vec3f>,

    // TODO place blocks, interact with tiles, move to new position, etc
    block_actions: Vec<ActionData>,
}

impl PlayerInput {
    pub fn new(id: PlayerId) -> Self {
        Self {
            id,
            move_intent: None,
            block_actions: Vec::new(),
        }
    }

    pub fn id(&self) -> &PlayerId {
        &self.id
    }

    pub fn move_intent(&self) -> Option<Vec3f> {
        self.move_intent
    }

    pub fn set_move_intent(&mut self, p: Vec3f) {
        self.move_intent = Some(p);
    }

    pub fn block_actions(&self) -> &[ActionData] {
        &self.block_actions
    }

    pub fn add_block_action(&mut self, a: ActionData) {
        self.block_actions.push(a);
    }
}

/// A chunk that's been newly loaded/generated and is ready to be inserted into the world.
#[derive(Clone, Debug)]
pub struct NewChunk {
    coord: ChunkCoord,
    chunk: ChunkState,
}

impl NewChunk {
    pub fn new(coord: ChunkCoord, chunk: ChunkState) -> Self {
        Self { coord, chunk }
    }

    pub fn coord(&self) -> ChunkCoord {
        self.coord
    }

    pub fn chunk(&self) -> &ChunkState {
        &self.chunk
    }

    pub fn into_chunk_state(self) -> ChunkState {
        self.chunk
    }
}
